package pers.whj.rpc.client;

import pers.whj.rpc.common.Config;
import pers.whj.rpc.protocol.RPCException;
import pers.whj.rpc.protocol.Request;
import pers.whj.rpc.protocol.Response;
import pers.whj.rpc.protocol.ServiceDescriptor;
import pers.whj.rpc.serialize.Decoder;
import pers.whj.rpc.serialize.Encoder;
import pers.whj.rpc.transport.TransportClient;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 调用远程服务的代理类
 */
public class RemoteInvoker implements InvocationHandler {
    private Encoder encoder;
    private Decoder decoder;
    private TransportClient transportClient;
    private final Class clazz;

    public RemoteInvoker(Class clazz, Encoder encoder, Decoder decoder, TransportClient transportClient) {
        this.encoder = encoder;
        this.decoder = decoder;
        this.transportClient = transportClient;
        this.clazz = clazz;
    }

    public void setEncoder(Encoder encoder) {
        this.encoder = encoder;
    }

    public void setDecoder(Decoder decoder) {
        this.decoder = decoder;
    }

    public void setTransportClient(TransportClient transportClient) {
        this.transportClient = transportClient;
    }

    /**
     * 调用远程服务，并返回结果
     *
     * @param proxy  the proxy instance that the method was invoked on
     * @param method the {@code Method} instance corresponding to
     *               the interface method invoked on the proxy instance.  The declaring
     *               class of the {@code Method} object will be the interface that
     *               the method was declared in, which may be a superinterface of the
     *               proxy interface that the proxy class inherits the method through.
     * @param args   an array of objects containing the values of the
     *               arguments passed in the method invocation on the proxy instance,
     *               or {@code null} if interface method takes no arguments.
     *               Arguments of primitive types are wrapped in instances of the
     *               appropriate primitive wrapper class, such as
     *               {@code java.lang.Integer} or {@code java.lang.Boolean}.
     * @return 返回Response类，表示请求结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws RPCException {
        Request request = new Request();
        request.setDescriptor(ServiceDescriptor.from(clazz, method));
        request.setParams(args);
        Response response = invokeRemote(request);
        if (response == null) {
            throw new RPCException("fail to invoke remote service!");
        }
        if (Config.DEBUG) {
            System.out.println("获取远程服务结果：" + response);
        }
        if (response.getCode() != Response.CODE_SUCCESS) {
            throw new RPCException(response.getErrorMsg());
        }
        return response.getData();
    }

    /**
     * 远程调用，返回结果
     *
     * @param request 封装的请求
     * @return 封装的请求结果
     */
    private Response invokeRemote(Request request) {
        Response response = null;
        try {
            request.initNumber();
            byte[] data = this.encoder.encode(request);
            data = this.transportClient.write(data);
            response = this.decoder.decode(data, Response.class);
        } catch (Exception e) {
            if (request == null) {
                response = new Response(Response.UNKNOWN_SOURCE);
            } else {
                response = new Response(request.getNumber());
            }
            response.setCode(Response.CODE_ERROR);
            response.setErrorMsg(e.getMessage());
        }
        return response;
    }
}
